﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using fastCSharp;
using System.Threading;
using fastCSharp.threading;

namespace OnlineJudge
{
    /// <summary>
    /// OJ服务
    /// </summary>
    [fastCSharp.code.cSharp.tcpServer(Service = "OJServer", IsIdentityCommand = true, IsServerAsynchronousReceive = false, IsCompress = true, IsMarkData = true, IsSegmentation = true, ClientSegmentationCopyPath = @"D:\OJ\OnlineJudge\OnlineJudgeClient\", VerifyMethodType = typeof(tcpClient.Server.tcpClient.timeVerifyMethod), ClientSendSleep = 1, ServerSendSleep = 1)]
    public partial class Server : fastCSharp.net.tcp.timeVerifyServer, IDisposable
    {
        /// <summary>
        /// 比赛提交标识
        /// </summary>
        private const int contestIdentity = 0x40000000;
        /// <summary>
        /// 回调池
        /// </summary>
        /// <typeparam name="valueType">数据类型</typeparam>
        sealed class callbackPool<valueType> where valueType : class
        {
            /// <summary>
            /// 数据集合
            /// </summary>
            private collection<keyValue<valueType, Action<valueType>>> items;
            /// <summary>
            /// 回调
            /// </summary>
            private Func<fastCSharp.net.returnValue<valueType>, bool> callback;
            /// <summary>
            /// 数据集合访问锁
            /// </summary>
            private int itemLock;
            /// <summary>
            /// 回调访问锁
            /// </summary>
            private int callbackLock;
            /// <summary>
            /// 添加回调数据
            /// </summary>
            public Action<valueType> PushHandle { get; private set; }
            /// <summary>
            /// 
            /// </summary>
            public callbackPool()
            {
                items = new collection<keyValue<valueType, Action<valueType>>>();
                PushHandle = push;
            }
            /// <summary>
            /// 设置回调
            /// </summary>
            /// <param name="callback"></param>
            public void Set(Func<fastCSharp.net.returnValue<valueType>, bool> callback)
            {
                Interlocked.Exchange(ref this.callback, callback);
                check();
            }
            /// <summary>
            /// 添加回调数据
            /// </summary>
            /// <param name="value"></param>
            private void push(valueType value)
            {
                Push(value, null);
            }
            /// <summary>
            /// 添加回调数据
            /// </summary>
            /// <param name="value"></param>
            /// <param name="callback"></param>
            public void Push(valueType value, Action<valueType> callback)
            {
                interlocked.CompareSetSleep0(ref itemLock);
                try
                {
                    items.Add(new keyValue<valueType, Action<valueType>>(value, callback));
                }
                finally { itemLock = 0; }
                if (this.callback != null) check();
            }
            /// <summary>
            /// 回调检测
            /// </summary>
            private void check()
            {
                while (items.Count != 0 && Interlocked.CompareExchange(ref callbackLock, 1, 0) == 0)
                {
                    try
                    {
                        do
                        {
                            interlocked.CompareSetSleep0(ref itemLock);
                            if (items.Count == 0)
                            {
                                itemLock = 0;
                                break;
                            }
                            keyValue<valueType, Action<valueType>> value = items.Unsafer.PopExpand();
                            itemLock = 0;
                            try
                            {
                                if (callback(value.Key))
                                {
                                    if (value.Value != null) value.Value(value.Key);
                                }
                                else
                                {
                                    output(null, "回调失败");
                                    callback = null;
                                    interlocked.CompareSetSleep0(ref itemLock);
                                    try
                                    {
                                        items.AddExpand(value);
                                    }
                                    finally { itemLock = 0; }
                                    return;
                                }
                            }
                            catch (Exception error)
                            {
                                output(error);
                            }
                        }
                        while (true);
                    }
                    finally { callbackLock = 0; }
                }
            }
        }
        /// <summary>
        /// 测试标识
        /// </summary>
        private struct judgeIdentity : IEquatable<judgeIdentity>
        {
            /// <summary>
            /// 测试标识
            /// </summary>
            public int Id;
            /// <summary>
            /// 测试类型
            /// </summary>
            public CompileType Type;
            /// <summary>
            /// 
            /// </summary>
            /// <returns></returns>
            public override int GetHashCode()
            {
                return Id ^ (byte)Type;
            }
            /// <summary>
            /// 
            /// </summary>
            /// <param name="obj"></param>
            /// <returns></returns>
            public override bool Equals(object obj)
            {
                return Equals((judgeIdentity)obj);
            }
            /// <summary>
            /// 
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public bool Equals(judgeIdentity other)
            {
                return ((Id ^ other.Id) | ((byte)Type ^ (byte)other.Type)) == 0;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        private CompilePool compilePool;
        /// <summary>
        /// 
        /// </summary>
        private JudgePool judgePool;
        /// <summary>
        /// 服务启动访问锁
        /// </summary>
        private object startLock = new object();
        /// <summary>
        /// 测试回调
        /// </summary>
        private callbackPool<JudgeItemBase> judgeCallbackPool;
        /// <summary>
        /// 测试回调
        /// </summary>
        private callbackPool<JudgeReportBase> reportCallbackPool;
        /// <summary>
        /// 未完成回调的测试集合
        /// </summary>
        private Dictionary<judgeIdentity, keyValue<int, JudgeReportBase>> reports;
        /// <summary>
        /// 测试回调
        /// </summary>
        private Action<JudgeItemBase> judgeCallback;
        /// <summary>
        /// OJ数据客户端
        /// </summary>
        private diantou.dataAccess.tcpClient.onlineJudgeData.tcpClient onlineJudgeData;
        /// <summary>
        /// 未完成回调的测试集合访问锁
        /// </summary>
        private int reportLock;
        /// <summary>
        /// 未完成数量
        /// </summary>
        private int judgeCount;
        /// <summary>
        /// 已编译未完成数量
        /// </summary>
        private int compiledCount;
        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            compilePool = null;
            judgePool = null;
            pub.Dispose(ref onlineJudgeData);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="tcpServer"></param>
        public override void SetCommandServer(fastCSharp.net.tcp.commandServer tcpServer)
        {
            base.SetCommandServer(tcpServer);
            JudgePoolConfig config = JudgePoolConfig.Default;
            compilePool = new CompilePool(config.CompileCount);
            judgePool = new JudgePool(config.JudgeCount);
            judgeCallbackPool = new callbackPool<JudgeItemBase>();
            reportCallbackPool = new callbackPool<JudgeReportBase>();
            reports = new Dictionary<judgeIdentity, keyValue<int, JudgeReportBase>>();
            judgeCallback = onJudgedCallback;
            AsyncStreamReader.BufferPoolCount = config.JudgeCount * 4 + config.CompileCount * 2 + 10;
            compilePool.FinishedExecute += onCompiled;
            judgePool.FinishedExecute += onJudged;
            judgePool.RunningExecute += onJudged;

            onlineJudgeData = new diantou.dataAccess.tcpClient.onlineJudgeData.tcpClient();
            fastCSharp.threading.threadPool.TinyPool.Start(checkProblemData);

            outputMemory();
        }
        /// <summary>
        /// 获取题目数据
        /// </summary>
        private void checkProblemData()
        {
            try
            {
                byte isError = 0;
                fastCSharp.net.returnValue<fastCSharp.keyValue<int, System.DateTime>[]> problemTimes = onlineJudgeData.problemTimes;
                if (problemTimes.Type == fastCSharp.net.returnValue.type.Success)
                {
                    foreach (keyValue<int, DateTime> item in problemTimes.Value)
                    {
                        if (JudgePool.Cache.CheckUpdate(item.Key, item.Value))
                        {
                            output(null, "OJ数据同步 ProblemId " + item.Key.toString());
                            fastCSharp.net.returnValue<OnlineJudge.problemData> data = onlineJudgeData.getData(item.Key);
                            if (data.Type != fastCSharp.net.returnValue.type.Success || !reworkProblemData(data.Value, item.Value))
                            {
                                output(null, "OJ数据同步失败 ProblemId " + item.Key.toString());
                                isError = 1;
                            }
                        }
                    }
                    if (isError == 0)
                    {
                        output(null, "OJ数据同步完成");
                        return;
                    }
                }
            }
            catch (Exception error)
            {
                output(error);
            }
            output(null, "OJ数据同步失败", ConsoleColor.Red);
            fastCSharp.threading.timerTask.Default.Add(checkProblemData, date.NowSecond.AddSeconds(5));
        }
        /// <summary>
        /// 启动服务
        /// </summary>
        /// <returns></returns>
        [fastCSharp.code.cSharp.tcpMethod]
        private JudgePoolConfig start()
        {
            output(null, "新的OJ客户端");
            return compilePool == null ? null : JudgePoolConfig.Default;
        }
        /// <summary>
        /// 内存状态输出
        /// </summary>
        private void outputMemory()
        {
            try
            {
                output(null, "内存占用 " + ((uint)(fastCSharp.diagnostics.process.GetProcessMemoryInfo().WorkingSetSize >> 20)).toString() + "M");
            }
            catch (Exception error)
            {
                output(error, null);
            }
            finally { fastCSharp.threading.timerTask.Default.Add(outputMemory, date.NowSecond.AddMinutes(10)); }
        }
        /// <summary>
        /// 编译完成事件
        /// </summary>
        /// <param name="compItem"></param>
        private void onCompiled(CompileBase compItem)
        {
            switch (compItem.CompileType)
            {
                case CompileType.CompileSolution:
                case CompileType.CompileSpecialJudge:
                    Interlocked.Decrement(ref judgeCount);
                    if (compItem.State == JudgeState.Compiled)
                    {
                        if (compItem.CompileType == CompileType.CompileSpecialJudge) JudgePool.Cache.AddSpecialJudge(compItem);
                        else if (compItem.CompileType == CompileType.CompileSolution) JudgePool.Cache.AddSolution(compItem);
                    }
                    else reworkProblemTime(compItem.ProblemId, DateTime.MinValue);
                    output(null, date.NowSecond.toString() + "[" + compiledCount.toString() + "/" + judgeCount.toString() + "] problemId[" + compItem.ProblemId.toString() + "] " + compItem.CompileType.ToString() + "Language[" + compItem.Language.ToString() + "] " + compItem.State.ToString() + " " + compItem.Message);
                    return;
            }
            JudgeReport judgeReport = compItem as JudgeReport;
            JudgeReportBase report = judgeReport.Copy<JudgeReportBase>();
            report.Results = null;
            report.TestIds = null;
            report.ProgramContent = null;
            if (report.IsClearMessage) report.Message = string.Empty;
            fastCSharp.threading.task.Tiny.Add(reportCallbackPool.PushHandle, report);
            if (judgeReport.State == JudgeState.Compiled)
            {
                Interlocked.Increment(ref compiledCount);
                judgeReport.Message = string.Empty;
                judgePool.Push(judgeReport);
            }
            else
            {
                Interlocked.Decrement(ref judgeCount);
                output(null, date.NowSecond.toString() + "[" + compiledCount.toString() + "/" + judgeCount.toString() + "] problemId[" + compItem.ProblemId.toString() + "] " + compItem.CompileType.ToString() + "[" + (compItem.CompileType == CompileType.Contest ? judgeReport.JudgeId - contestIdentity : judgeReport.JudgeId).toString() + "] Language[" + judgeReport.Language.ToString() + "] " + judgeReport.State.ToString() + " " + judgeReport.Message);
            }
        }
        /// <summary>
        /// 测试完成事件
        /// </summary>
        /// <param name="judgeItem"></param>
        private void onJudged(JudgeItem judgeItem)
        {
            keyValue<int, JudgeReportBase> report;
            interlocked.CompareSetSleep0(ref reportLock);
            try
            {
                if (reports.TryGetValue(new judgeIdentity { Id = judgeItem.JudgeId, Type = judgeItem.CompileType }, out report))
                {
                    ++report.Key;
                    reports[new judgeIdentity { Id = judgeItem.JudgeId, Type = judgeItem.CompileType }] = report;
                }
                else reports.Add(new judgeIdentity { Id = judgeItem.JudgeId, Type = judgeItem.CompileType }, new keyValue<int, JudgeReportBase>(1, null));
            }
            finally { reportLock = 0; }
            judgeCallbackPool.Push(judgeItem.Copy<JudgeItemBase>(), judgeCallback);
            //output(null, judgeItem.JudgeId.toString() + " Test: " + judgeItem.TestId.toString() + " " + judgeItem.Result.ToString());
        }
        /// <summary>
        /// 测试回调完成事件
        /// </summary>
        /// <param name="judgeItem"></param>
        private void onJudgedCallback(JudgeItemBase judgeItem)
        {
            JudgeReportBase judgeReport = null;
            interlocked.CompareSetSleep0(ref reportLock);
            try
            {
                keyValue<int, JudgeReportBase> report = reports[new judgeIdentity { Id = judgeItem.JudgeId, Type = judgeItem.CompileType }];
                if (--report.Key == 0)
                {
                    judgeReport = report.Value;
                    reports.Remove(new judgeIdentity { Id = judgeItem.JudgeId, Type = judgeItem.CompileType });
                }
                else reports[new judgeIdentity { Id = judgeItem.JudgeId, Type = judgeItem.CompileType }] = report;
            }
            finally { reportLock = 0; }
            if (judgeReport != null)
            {
                fastCSharp.threading.task.Tiny.Add(reportCallbackPool.PushHandle, judgeReport);
                output(null, date.NowSecond.toString() + "[" + compiledCount.toString() + "/" + judgeCount.toString() + "] problemId[" + judgeReport.ProblemId.toString() + "] " + judgeReport.CompileType.ToString() + "[" + (judgeReport.CompileType == CompileType.Contest ? judgeReport.JudgeId - contestIdentity : judgeReport.JudgeId).toString() + "] " + judgeReport.State.ToString(), ConsoleColor.Red);
            }
        }
        /// <summary>
        /// 测试结束事件
        /// </summary>
        /// <param name="judgeReport"></param>
        private void onJudged(JudgeReport judgeReport)
        {
            JudgeReportBase report = judgeReport.Copy<JudgeReportBase>();
            if (judgeReport.CompileType != CompileType.Tutorial) report.ExtInfo = null;
            Interlocked.Decrement(ref compiledCount);
            Interlocked.Decrement(ref judgeCount);
            report.ProgramContent = null;
            report.TestIds = null;
            report.Results = null;
            keyValue<int, JudgeReportBase> count;
            interlocked.CompareSetSleep0(ref reportLock);
            try
            {
                if (reports.TryGetValue(new judgeIdentity { Id = report.JudgeId, Type = judgeReport.CompileType }, out count))
                {
                    count.Value = report;
                    reports[new judgeIdentity { Id = report.JudgeId, Type = judgeReport.CompileType }] = count;
                    report = null;
                }
            }
            finally { reportLock = 0; }
            if (report != null)
            {
                if (report.IsClearMessage) report.Message = string.Empty;
                fastCSharp.threading.task.Tiny.Add(reportCallbackPool.PushHandle, report);
                output(null, date.NowSecond.toString() + "[" + compiledCount.toString() + "/" + judgeCount.toString() + "] problemId[" + judgeReport.ProblemId.toString() + "] " + judgeReport.CompileType.ToString() + "[" + (judgeReport.CompileType == CompileType.Contest ? judgeReport.JudgeId - contestIdentity : judgeReport.JudgeId).toString() + "] RetryCount[" + judgeReport.RetryCount.toString() + "] " + judgeReport.State.ToString(), ConsoleColor.Red);
            }
            if (judgeReport.State == JudgeState.Complete)
            {
                if (judgeReport.Result == JudgeResult.Accepted)
                {
                    output(null, date.NowSecond.toString() + " problemId[" + judgeReport.ProblemId.toString() + "] " + judgeReport.CompileType.ToString() + "[" + (judgeReport.CompileType == CompileType.Contest ? judgeReport.JudgeId - contestIdentity : judgeReport.JudgeId).toString() + "]  Memory[" + judgeReport.MemoryUse.toString() + "] Time[" + judgeReport.TimeUse.toString() + "] Language[" + judgeReport.Language.ToString() + "]", ConsoleColor.Red);
                }
                else output(null, date.NowSecond.toString() + " problemId[" + judgeReport.ProblemId.toString() + "] " + judgeReport.CompileType.ToString() + "[" + (judgeReport.CompileType == CompileType.Contest ? judgeReport.JudgeId - contestIdentity : judgeReport.JudgeId).toString() + "] " + judgeReport.Result.ToString(), ConsoleColor.Red);
            }
        }
        /// <summary>
        /// 添加测试
        /// </summary>
        /// <param name="item"></param>
        [fastCSharp.code.cSharp.tcpMethod]
        private void judge(JudgeReportBase item)
        {
            Interlocked.Increment(ref judgeCount);
            compilePool.Push(item.Copy<JudgeReport, JudgeReportBase>());
        }
        /// <summary>
        /// 测试回调
        /// </summary>
        /// <param name="onJudged"></param>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousCallback = true, IsClientAsynchronous = true, IsClientCallbackTask = false, IsClientSynchronous = false, IsKeepCallback = true)]
        private void onJudged(Func<fastCSharp.net.returnValue<JudgeItemBase>, bool> onJudged)
        {
            judgeCallbackPool.Set(onJudged);
        }
        /// <summary>
        /// 测试回调
        /// </summary>
        /// <param name="onJudged"></param>
        [fastCSharp.code.cSharp.tcpMethod(IsServerAsynchronousCallback = true, IsClientAsynchronous = true, IsClientCallbackTask = false, IsClientSynchronous = false, IsKeepCallback = true)]
        private void onJudged(Func<fastCSharp.net.returnValue<JudgeReportBase>, bool> onJudged)
        {
            reportCallbackPool.Set(onJudged);
        }
        /// <summary>
        /// 输入数据格式化字符串检测
        /// </summary>
        /// <param name="formatString"></param>
        /// <returns></returns>
        [fastCSharp.code.cSharp.tcpMethod]
        private bool checkFormatString(string formatString)
        {
            return JudgePool.Cache.CheckFormatString(formatString);
        }
        /// <summary>
        /// 添加或者修改测试数据
        /// </summary>
        /// <param name="problemId"></param>
        /// <param name="testId"></param>
        /// <param name="inData"></param>
        /// <param name="outData"></param>
        /// <returns></returns>
        [fastCSharp.code.cSharp.tcpMethod]
        private void testData(int problemId, int testId, string inData, string outData, DateTime problemTime)
        {
            if (JudgePool.Cache.AddTest(problemId, testId, inData, outData)) reworkProblemTime(problemId, problemTime);
        }
        /// <summary>
        /// 删除测试数据
        /// </summary>
        /// <param name="problemId"></param>
        /// <param name="testId"></param>
        /// <returns></returns>
        [fastCSharp.code.cSharp.tcpMethod]
        private void deleteTestData(int problemId, int testId, DateTime problemTime)
        {
            if (JudgePool.Cache.DelTest(problemId, testId)) reworkProblemTime(problemId, problemTime);
        }
        /// <summary>
        /// 删除特判程序
        /// </summary>
        /// <param name="problemId"></param>
        /// <returns></returns>
        [fastCSharp.code.cSharp.tcpMethod]
        private void deleteSpecialProgarm(int problemId, DateTime problemTime)
        {
            if (JudgePool.Cache.DelSpecialJudge(problemId)) reworkProblemTime(problemId, problemTime);
        }
        /// <summary>
        /// 设置问题测试参数
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        [fastCSharp.code.cSharp.tcpMethod]
        private void setProblemJudgeSetting(JudgeSetting value, DateTime problemTime)
        {
            if (JudgePool.Cache.AddConfig(value)) reworkProblemTime(value.ProblemId, problemTime);
        }
        /// <summary>
        /// 更新题目时间
        /// </summary>
        /// <param name="problemId"></param>
        /// <param name="problemTime">问题更新时间</param>
        /// <returns></returns>
        [fastCSharp.code.cSharp.tcpMethod]
        private bool reworkProblemTime(int problemId, DateTime problemTime)
        {
            return JudgePool.Cache.GetLastUpdate(problemId) != DateTime.MinValue && JudgePool.Cache.SetLastUpdate(problemId, problemTime);
        }
        /// <summary>
        /// 更新OJ数据
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [fastCSharp.code.cSharp.tcpMethod]
        private bool reworkProblemData(OnlineJudge.problemData data, DateTime problemTime)
        {
            if (JudgePool.Cache.AddConfig(data.JudgeSetting))
            {
                int problemId = data.JudgeSetting.ProblemId;
                JudgePool.Cache.ClearTestNotIn(problemId, data.Tests.getArray(value => value.TestId));
                foreach (OnlineJudge.problemData.testData testData in data.Tests)
                {
                    if (!JudgePool.Cache.AddTest(problemId, testData.TestId, testData.Input, testData.Output)) return false;
                }
                if (JudgePool.Cache.SetLastUpdate(problemId, problemTime))
                {
                    JudgeReportBase solution = data.Solution;
                    if (solution != null)
                    {
                        solution.ProblemId = problemId;
                        solution.CompileType = CompileType.CompileSolution;
                        judge(solution);
                    }
                    JudgeReportBase specialJudge = data.SpecialJudge;
                    if (specialJudge != null)
                    {
                        specialJudge.ProblemId = problemId;
                        specialJudge.CompileType = CompileType.CompileSpecialJudge;
                        judge(specialJudge);
                    }
                    return true;
                }
            }
            return false;
        }


        ///// <summary>
        ///// 输出锁
        ///// </summary>
        //private static int outputLock;
        /// <summary>
        /// 输出任务
        /// </summary>
        private static taskQueue outputQueue = new taskQueue();
        /// <summary>
        /// 输出错误信息
        /// </summary>
        /// <param name="error"></param>
        /// <param name="message"></param>
        /// <param name="color"></param>
        private static void output(Exception error, string message = null, ConsoleColor? color = null)
        {
            outputQueue.Add(() =>
            {
                if (error == null) log.Default.Add(message, new System.Diagnostics.StackFrame(), false);
                else log.Default.Add(error, message, false);
                ConsoleColor oldColor = ConsoleColor.Black;
                //interlocked.CompareSetSleep0(ref outputLock);
                if (color != null)
                {
                    oldColor = Console.ForegroundColor;
                    Console.ForegroundColor = color.Value;
                }
                if (error != null) Console.WriteLine(error.ToString());
                if (message != null) Console.WriteLine(message);
                if (color != null) Console.ForegroundColor = oldColor;
                //outputLock = 0;
            });
        }
    }
}
